package cn.jasonone.him.jmx;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import javax.annotation.Resource;
import javax.management.Notification;
import javax.management.NotificationBroadcasterSupport;

import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

import com.corundumstudio.socketio.SocketIOClient;
import com.corundumstudio.socketio.SocketIOServer;

import cn.hutool.json.JSONUtil;
import cn.jasonone.him.HimConst;
import cn.jasonone.him.HimProperties;
import cn.jasonone.him.event.HimAuthorizationAfterEvent;
import cn.jasonone.him.event.HimAuthorizationBeforeEvent;
import cn.jasonone.him.event.HimEvent;
import cn.jasonone.him.event.HimExceptionEvent;
import cn.jasonone.him.event.HimMessageEvent;
import cn.jasonone.him.model.HimUserInfo;
import cn.jasonone.him.service.HimService;
import cn.jasonone.him.util.HimUtil;

@Component
public class Him extends NotificationBroadcasterSupport implements HimMBean {
	@Resource
	private HimService himService;
	@Resource
	private SocketIOServer server;

	private String userToString(HimUserInfo userInfo) {
		return "id:" + userInfo.getId() + ",username:" + userInfo.getUsername();
	}

	@Override
	public List<String> getClients() {
		List<String> list = new ArrayList<>();
		Collection<SocketIOClient> clients = server.getAllClients();
		for (SocketIOClient client : clients) {
			list.add(userToString(HimUtil.getUserInfo(client)));
		}
		return list;
	}

	@Override
	public List<String> getGroups() {
		List<String> list = new ArrayList<>();
		server.getAllClients().stream().map(c -> c.getAllRooms()).forEach(r -> list.addAll(r));
		return list.stream().distinct().collect(Collectors.toList());
	}

	@Override
	public List<String> getGroups(String userId) {
		List<String> list = new ArrayList<>();
		Collection<SocketIOClient> clients = server.getAllClients();
		Stream<Set<String>> stream = clients.stream().map(client -> HimUtil.getUserInfo(client))
				.filter(c -> c.getId().equals(userId)).map(c -> server.getClient(c.getSessionId()).getAllRooms());
		stream.forEach(g -> list.addAll(g));
		return list;
	}

	@Override
	public List<String> getGroupMembers(String groupId) {
		return this.himService.findGroupById(groupId).getList().stream().map(u -> userToString(u))
				.collect(Collectors.toList());
	}

	@Override
	public List<String> getFriend(String userId) {
		return this.himService.findFriends(userId).stream().map(u -> JSONUtil.toJsonStr(u))
				.collect(Collectors.toList());
	}

	private static final Map<String, Long> seqs = new HashMap<>(5);
	@Resource
	private HimProperties himProperties;

	/**
	 * 权限鉴定事件-前
	 * 
	 * @param event 事件对象
	 */
	@Async
	@EventListener
	public void listenerAuthorizationBefore(HimAuthorizationBeforeEvent event) {
		sendNotification(event, event.getData().getUrlParams());
	}

	/**
	 * 权限鉴定事件-后
	 * 
	 * @param event 事件对象
	 */
	@Async
	@EventListener
	public void listenerAuthorizationAfter(HimAuthorizationAfterEvent event) {
		sendNotification(event);
	}

	/**
	 * 用户连接事件
	 * 
	 * @param event 事件对象
	 */
	@Async
	@EventListener(condition = "#event.type == '" + HimConst.CONNECT_EVENT + "'")
	public void listenerConnect(HimEvent<HimUserInfo> event) {
		Map<String, Object> userData = new HashMap<>();
		HimUserInfo userInfo = event.getData();
		if (userInfo != null) {
			userData.put("id", userInfo.getId());
			userData.put("username", userInfo.getUsername());
			userData.put("sessionId", userInfo.getSessionId().toString());
			userData.put("status", userInfo.getStatus().toString());
			sendNotification(event, userData);
		}
	}

	/**
	 * 用户离线事件
	 * 
	 * @param event 事件对象
	 */
	@Async
	@EventListener(condition = "#event.type == '" + HimConst.DISCONNECT_EVENT + "'")
	public void listenerDisconnect(HimEvent<HimUserInfo> event) {
		Map<String, Object> userData = new HashMap<>();
		HimUserInfo userInfo = event.getData();
		if (userInfo != null) {
			userData.put("id", userInfo.getId());
			userData.put("username", userInfo.getUsername());
			userData.put("sessionId", userInfo.getSessionId().toString());
			userData.put("status", userInfo.getStatus().toString());
		}
		sendNotification(event, userData);
	}

	/**
	 * 用户消息事件
	 * 
	 * @param event 事件对象
	 */
	@Async
	@EventListener
	public void listenerMessage(HimMessageEvent event) {
		Map<String, Object> userData = new HashMap<>();
		if (event.getData()!=null) {
			userData.put("id", event.getData().getId());
			userData.put("message", event.getData().getMessage());
		}
		sendNotification(event, userData);
	}

	/**
	 * 异常事件
	 * 
	 * @param event 事件对象
	 */
	@Async
	@EventListener
	public void listenerException(HimExceptionEvent event) {
		sendNotification(event);
	}

	/**
	 * 发送通知
	 * 
	 * @param event 事件对象
	 */
	public void sendNotification(HimEvent<?> event) {
		sendNotification(event.getType(), event.getSource(), event.getContent(), event.getData());
	}

	/**
	 * 发送通知
	 * 
	 * @param event    事件对象
	 * @param userData 用户数据
	 */
	public void sendNotification(HimEvent<?> event, Object userData) {
		sendNotification(event.getType(), event.getSource(), event.getContent(), userData);
	}

	/**
	 * 发送通知
	 * 
	 * @param event    事件对象
	 * @param content  事件内容
	 * @param userData 用户数据
	 */
	public void sendNotification(HimEvent<?> event, String content, Object userData) {
		sendNotification(event.getType(), event.getSource(), content, userData);
	}

	/**
	 * 发送通知
	 * 
	 * @param type     通知类型
	 * @param source   发送源
	 * @param content  通知内容
	 * @param userData 用户数据
	 */
	public void sendNotification(String type, Object source, String content, Object userData) {
		HimJmxProperties jmxProperties = himProperties.getJmx();
		// 检查是否开启通知
		if (jmxProperties == null || jmxProperties.isNotificationEnable() == false) {
			return;
		}
		long seq = 1L;
		if (seqs.containsKey(type)) {
			seq = seqs.get(type) + 1;
		}
		seqs.put(type, seq);
		Notification n = new Notification(// 创建一个信息包
				type, // 给这个Notification起个名称
				source.getClass().getName(), // 由谁发出的Notification
				seq, // 一系列通知中的序列号,可以设任意数值
				System.currentTimeMillis(), // 发出时间
				content);// 发出的消息文本
		n.setUserData(userData);
		// 发出去
		sendNotification(n);
	}

}
